﻿using AiMoWindows.CodeScripts.Init;
using AiMoWindows.Compiler;
using AiMoWindows.Entrance;
using AiMoWindows.GHAiMo;
using AiMoWindows.Localization;
using AiMoWindows.MainActions.Mode;
using System.IO;
using System.Text;

namespace AiMoWindows.CodeScripts
{
    class ScriptSource : ICompilerScript
    {
        /// <summary>
        /// 引用的代码
        /// </summary>
        readonly UsingCodeController m_UsingCode = new UsingCodeController();
        /// <summary>
        /// 只读的代码
        /// </summary>
        readonly ReadonlyCodeBlocks m_ReadonlyCodes = new ReadonlyCodeBlocks();

        /// <summary>
        /// Main函数里运行的代码
        /// 可能为空，为空则不是导出AiMo的dll
        /// </summary>
        public CodeBlock MainCode { get; } = CodeBlock.Default;

        /// <summary>
        /// 其他函数里运行的代码
        /// </summary>
        public CodeBlock AdditionalFunctionCode { get; } = CodeBlock.Default;

        /// <summary>
        /// 其他类里运行的代码
        /// </summary>
        public CodeBlock AdditionalClassCode { get; } = CodeBlock.Default;
        public ScriptAttributes Attributes { get; set; } = new ScriptAttributes();

        /// <summary>
        /// 引用的文件
        /// </summary>
        public string[] ReferenceFiles { get; private set; } = Array.Empty<string>();
        public bool IsEmpty_Main => MainCode.IsEmpty;
        public bool IsEmpty => MainCode.IsEmpty && AdditionalClassCode.IsEmpty && AdditionalFunctionCode.IsEmpty;
        /// <summary>
        /// 是正常编译模式
        /// </summary>
        public ScriptComplileMode ScriptsMode { get; private set; }
        /// <summary>
        /// 根据 CompileMode 实时读取不同的AllCodeBlock
        /// </summary>
        CodeBlock[] AllCodeBlocks
        {
            get
            {

                m_ReadonlyCodes.GetCurrentCodeBlocks(ScriptsMode, out CodeBlock beforeMain, out CodeBlock beforeFunc, out CodeBlock beforeClass);
                switch (ScriptsMode)
                {
                    case ScriptComplileMode.ExternalAssemblies:
                        return new CodeBlock[]
                        {
                            m_UsingCode.CurrentUsingCode(ScriptsMode),
                            AdditionalClassCode,
                        };
                    default:
                    case ScriptComplileMode.GrasshopperScript:
                    case ScriptComplileMode.RhinoScript:
                        return new CodeBlock[]
                        {
                            m_UsingCode.CurrentUsingCode(ScriptsMode),
                            beforeMain,
                            MainCode,
                            beforeFunc,
                            AdditionalFunctionCode,
                            beforeClass,
                            AdditionalClassCode,
                        };
                }
            }
        }

        /// <summary>
        /// 初始化时的作用，就是创建Default的代码
        /// </summary>
        /// <param name="scriptsMode"></param>
        /// <param name="referenceFiles"></param>
        /// <param name="emptyScript"></param>
        public ScriptSource(ScriptComplileMode scriptsMode, string[]? referenceFiles, bool emptyScript)
        {
            ScriptsMode = scriptsMode;
            if (referenceFiles != null && referenceFiles.Length > 0)
            {
                AddReferenceFiles(referenceFiles);
            }
            Attributes.Author = GetDefaultAuthor();
            if (emptyScript)
            {
                Attributes.Description = GetDefaultDescription(null);
            }
            else
            {
                // 新建带有默认案例的代码

                // 从GH中打开保存的代码
                // 从Grasshopper中打开，并且模式是Grasshopper，且带有GH的传送文件
                if (scriptsMode == ScriptComplileMode.GrasshopperScript &&
                    AiMoProgram.Instance.AiMoExecuterMode == ExecuterMode.Grasshopper &&
                    (!string.IsNullOrEmpty(AiMoProgram.Instance.ScriptFileFromGH)) &&
                    GHScriptIO.ReadFromCache(AiMoProgram.Instance.ScriptFileFromGH,
                    out string[] usings, out string[] mains, out string[] funcs, out string[] classes, out string[] externalReferences, out _, out _))
                {
                    ScriptsMode = ScriptComplileMode.GrasshopperScript;
                    m_UsingCode.Update(usings);
                    MainCode.UpdateLines(mains);
                    AdditionalFunctionCode.UpdateLines(funcs);
                    AdditionalClassCode.UpdateLines(classes);
                    AddReferenceFiles(externalReferences);
                }
                else
                {
                    //Attributes.Description = GetDefaultDescription(null);
                    //return;
                    // 不是从GH中打开保存的代码，根据情况判断获取随机代码
                    ExampleInitCodes.GetRandomExample(scriptsMode, out string[]? mains2, out string[]? methods, out string[]? description, out string[]? usings2);
                    m_UsingCode.Update(usings2);
                    MainCode.UpdateLines(mains2);
                    AdditionalFunctionCode.UpdateLines(methods);
                    Attributes.Description = GetDefaultDescription(description);

                }
            }
        }
        public string GetReferenceExistedChangedHash()
        {
            string hash = "";
            foreach (string path in ReferenceFiles)
            {
                if (string.IsNullOrEmpty(path)) continue;
                hash += $"[{path}]:[{File.Exists(path)}];";
            }
            return hash;
        }

        string[] ICompilerScript.GetReferenceAssemblies() => ReferenceFiles;
        /// <summary>
        /// 获取最终编译的string
        /// </summary>
        CompileCodeUnit[] ICompilerScript.GetCodeForCompiler()
        {
            StringBuilder builder = new StringBuilder();
            //让代码补全行数一致
            builder.AppendLine("// 01");
            builder.AppendLine("// 02");
            foreach (string _ in ReferenceFiles) builder.AppendLine("// ref");

            foreach (CodeBlock block in AllCodeBlocks)
            {
                AppendCodeBlock(builder, block);
            }
            return new CompileCodeUnit[] { new CompileCodeUnit(builder.ToString(), "") };
        }
        #region GH Special

        public GH_ParamSyntaxNodeInfo[] GH_ParamsInfos { get; set; } = Array.Empty<GH_ParamSyntaxNodeInfo>();
        public bool HasPublicCustomizedClasses { get; set; } = false;

        void ICompilerScript.OnCompileSuccess(CompiledResult result)
        {
            //当是GH时，附带AiMoScript给AiMoGH读取代码
            if (AiMoProgram.Instance.AiMoExecuterMode == ExecuterMode.Grasshopper
                && ScriptsMode == ScriptComplileMode.GrasshopperScript)
            {
                GHScriptIO.WriteToCache(
                    m_UsingCode.CurrentUsingCode(ScriptsMode).Lines,
                    MainCode.Lines,
                    AdditionalFunctionCode.Lines,
                    AdditionalClassCode.Lines,
                    ReferenceFiles,
                    GH_ParamsInfos,
                    GHScriptIO.GetSameScriptPath(result.DllPath),
                    HasPublicCustomizedClasses
                    );
            }
        }
        #endregion
        public string GetCodeForScriptEditor()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine(Local.RunningInChinese ? $"//AiMo 版本：{Version.version}" : $"//AiMo Release Version: {Version.version}");
            builder.AppendLine(Local.RunningInChinese ?
                "//在using前添加外部文件引用，比如 #r \"<这里是外部文件完整路径>\" [F5] 刷新" :
                "//Add external reference before all usings. Such as #r \"<Here is the full path of assembly file>\" [F5] to refresh");
            foreach (string referenceFile in ReferenceFiles)
            {
                builder.AppendLine($"#r \"{referenceFile}\"");
            }
            foreach (CodeBlock block in AllCodeBlocks)
            {
                AppendCodeBlock(builder, block);
            }
            return builder.ToString();
        }

        /// <summary>
        /// 设置没有// 的Author和Description
        /// </summary>
        public void SetAuthorAndDescription(string authors, string descriptions)
        {
            Attributes.Author = authors;
            Attributes.Description = descriptions;
        }

        /// <summary>
        /// 设定Usings
        /// 通过这个方法修改UsingCodes
        /// 不会添加非using开头的
        /// 并且对Using排序
        /// 这里可能不包含Using AiMoCore
        /// </summary>
        public void SetUsingCode(IEnumerable<string> lines)
        {
            m_UsingCode.Update(lines);
        }
        /// <summary>
        /// 设定Usings
        /// 通过这个方法修改UsingCodes
        /// 不会添加非using开头的
        /// 并且对Using排序
        /// 这里可能不包含Using AiMoCore
        /// </summary>
        public void SetUsingCode(string text)
        {
            SetUsingCode(Utils.Split(Utils.FixNewlines(text)));
        }
        public string GetUsingsCode()
        {
            return m_UsingCode.CurrentUsingCode(ScriptsMode).ToString();
        }
        public string GetCompatibleIOUsings()
        {
            List<string> result = new List<string>();
            foreach (string line in m_UsingCode.CurrentUsingCode(ScriptsMode).Lines)
            {
                if (line.Contains("using AiMoCore;")) continue;
                if (line.StartsWith("//")) continue;
                result.Add(line);
            }
            return string.Join("\r\n", result);
        }
        internal void ComputeRunningHashCode(StringBuilder writer)
        {
            writer.Append("Main");
            writer.Append(MainCode.ToString());
            writer.Append("AdditionalClass");
            writer.Append(AdditionalClassCode.ToString());
            writer.Append("AdditionalFunc");
            writer.Append(AdditionalFunctionCode.ToString());
            writer.Append("Usings");
            writer.Append(m_UsingCode.CurrentUsingCode(ScriptsMode).ToString());
            writer.Append("ReferenceAssemblies");
            writer.Append(ReferenceFiles.Length);
            writer.Append("Author");
            writer.Append(Attributes.Author);
            writer.Append("Description");
            writer.Append(Attributes.Description);
            foreach (string file in ReferenceFiles)
            {
                writer.Append(file);
            }
        }
        /// <summary>
        /// 将参考DLL/RHP/添加到编译器中。
        /// </summary>
        public void AddReferenceFiles(string[] dllFiles)
        {
            HashSet<string> newDllFiles = ReferenceFiles.Concat(dllFiles).ToHashSet();
            ReferenceFiles = newDllFiles.ToArray();
        }
        public void UpdateScriptMode(ScriptComplileMode mode, out bool modeChanged)
        {
            modeChanged = mode != ScriptsMode;
            ScriptsMode = mode;
        }
        public void SetReferenceFiles(string[] dllFiles)
        {
            ReferenceFiles = dllFiles.ToHashSet().ToArray();
        }

        string GetDefaultDescription(string[]? description)
        {
            string line = Local.Get("Create date: ") + $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}]";
            if (description == null)
            {
                return line;
            }
            return string.Join("\r\n", description.Append(line));
        }
        string GetDefaultAuthor()
        {
            return Server.ServerHelper.WindowsUserName;
        }
        public string GetScriptHash() => Utils.ComputeScriptHash(this);
        static void AppendCodeBlock(StringBuilder builder, CodeBlock codeBlock)
        {
            if (codeBlock.IsEmpty) return;
            foreach (var line in codeBlock.Lines)
            {
                builder.AppendLine(line);
            }
        }

    }
}
